/** \file gr_containers.h Utility containers compatible with stl.
.*/
#pragma once

#include "gr_env_types.h"
#include "gr_shims.h"
#include <stack>
#include <memory>
#include <exception>

namespace gr{

    /** Unsafe view. STL-container like interface to elements of type T stored
    * in an orderd buffer but possilby interleaved with other data.
    */
    class UnsafeView
    {
    public:
        typedef size_t offset_size;
        typedef size_t sample_size;

        /** Iterator to an unsafe view. */
        template<class T>
        class typed_iterator
        {
        public:
            typed_iterator(uint8_t* data, offset_size offset):data_(data), offset_(offset) 
            {}
            bool operator!=(const typed_iterator& i){return (data_ != i.data_);}
            void operator++(){data_ += offset_;}
            T*   operator->(){return std::static_cast<T*>(data_);}
            T&   operator*(){return *(std::static_cast<T*>(data_));}
        private:
            uint8_t*    data_;   //> Pointer to first element of data
            offset_size offset_; //> Offset in bytes between consequent elements
        };

        explicit UnsafeView(uint8_t* data, offset_size offset, sample_size sampleSize, size_t size):
            data_(data), offset_(offset), sampleSize_(sampleSize), size_(size) {}

        UnsafeView(const UnsafeView& other){*this = other;}
        UnsafeView():data_(nullptr), offset_(0), sampleSize_(0), size_(0){}

        template<class T>
        T& at(size_t i){return *(std::static_cast<T*>(data_ + i * offset_));}

        size_t size(){return size_;}
        size_t offsetSize(){return offset_;}
        size_t sampleSize(){return sampleSize_;}
        uint8_t* data(){return data_;}

        template<class T>
        typed_iterator<T> begin()
        {
            if(sizeof(T) != sampleSize_) 
            {
                std::string msg = "Types not of equal size. Could not return typed iterator for " + typeid(T).name;
                throw std::runtime_error(msg);
            }
            return typed_iterator<T>(data_, offset_);
        }
        template<class T>
        typed_iterator<T> end()
        {
            if(sizeof(T) != sampleSize_) 
            {
                std::string msg = "Types not of equal size. Could not return typed iterator for " + typeid(T).name;
                throw std::runtime_error(msg);
            }
            return typed_iterator<T>(data_ + size_ * offset_, offset_);
        }

        UnsafeView& operator=(const UnsafeView& other)
        {
            data_ = other.data_;
            offset_ = other.offset_;
            sampleSize_ = other.sampleSize_;
            size_ = other.size_;
            return *this;
        }

    private:
        uint8_t*    data_;       //> Pointer to first element of data
        offset_size offset_;     //> Offset in bytes between consequent elements
        sample_size sampleSize_; //> size of one sample in bytes
        size_t      size_;       //> number of samples stored in referred buffer
    };

    template<class T>
    UnsafeView unsafeViewOfContainer(T& container)
    {
        size_t singleSize = sizeof(typename T::value_type);
        return UnsafeView(reinterpret_cast<uint8_t*>(&container[0]), singleSize, singleSize, container.size());
    }

    /** Contains a state to generate unique keys. */
    class LinearKeyGenerator
    {
        integer_key keySeed_;
        public:
        LinearKeyGenerator(integer_key startIndex  = 1):keySeed_(startIndex){}
        integer_key operator()(){return keySeed_++;}

    };

    /** Manages a collection of locally unique keys.*/
    class KeyWarehouse
    {
    public:
        integer_key newKey()
        {
            integer_key result;
            if(isEmpty(freeKeys_))
            {
                result = generator_();
            }
            else
            {
                result = freeKeys_.top();
                freeKeys_.pop();
            }
            return result;
        }
        void freeKey(integer_key key)
        {
            freeKeys_.push(key);
        }
    private:
        LinearKeyGenerator generator_;
        std::stack<integer_key> freeKeys_;
    };

    
    class StringKeyGeneratorImpl;
    /** Return a integer_key for given input string. */
    class StringKeyGenerator
    {   
        StringKeyGeneratorImpl* map_;
        public:
        StringKeyGenerator();
        //StringKeyGenerator(LinearKeyGenerator& gen); // TODO: Should be a thread safe implementation.
        ~StringKeyGenerator();
        integer_key getKey(const char* string);
    };

}
